#include <iostream>
#include <unistd.h>
#include <vector>
#include <mutex>
#include "Thread.hpp"
using namespace std;
using namespace ThreadModule;

const int threadnum=4;
int g_tickets=10000;



//解决方案四:c++11提供std::mutex
class ThreadData
{
public:
    ThreadData(int& g_tickets,const string& threadname,std::mutex& mutex)
    :_tickets(g_tickets)
    ,_threadname(threadname)
    ,_mutex(mutex)
    ,_total(0)
    {

    }
public:
    int& _tickets;//总票数,保证所有的线程都会引用同一个全局的g_tickets;
    string _threadname;//每个线程的线程名
    int _total;//每个线程获得的总票数
    std::mutex& _mutex;//各个线程共同抢占同一把局部互斥锁
};

void BuyTicket(ThreadData* td)
{
    while(true)
    {   
        td->_mutex.lock();
        if(td->_tickets>0)//1.逻辑判断访问共享资源
        {
            usleep(1000);
            printf("%s get ticket number:%d\n",td->_threadname.c_str(),td->_tickets);//2. IO访问共享资源
            td->_tickets--;//3.算术运算访问共享资源
            td->_mutex.unlock();
            td->_total++;
        }
        else
        {
            td->_mutex.unlock();
            break;
        }
    }
}

int main()
{

    std::mutex mutex;

    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> tds;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        ThreadData* td=new ThreadData(g_tickets,threadname,mutex);
        threads.emplace_back(BuyTicket,td,threadname);
        tds.push_back(td);
    }

    for(auto& thread:threads)
    {
        thread.Start();
    }

     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
  
    for(auto td:tds)
    {
        cout<<td->_threadname<<" get total tickets:"<<td->_total<<endl;
        delete td;
    }
    return 0;
}


//解决方案三:利用对象的生命周期控制互斥锁资源,对象构造申请局部互斥锁,对象析构时释放互斥锁,RAII风格互斥锁安全可靠
/*
class LockGuard
{
public:
    LockGuard(pthread_mutex_t* mutex)
    {
        _mutex=mutex;
        pthread_mutex_lock(mutex);//构造LockGuard对象时申请互斥锁
    }

    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);//析构LockGuard对象时释放互斥锁
    }

private:
    pthread_mutex_t* _mutex;
};

class ThreadData
{
public:
    ThreadData(int& g_tickets,const string& threadname,pthread_mutex_t& mutex)
    :_tickets(g_tickets)
    ,_threadname(threadname)
    ,_mutex(mutex)
    ,_total(0)
    {

    }
public:
    int& _tickets;//总票数,保证所有的线程都会引用同一个全局的g_tickets;
    string _threadname;//每个线程的线程名
    int _total;//每个线程获得的总票数
    pthread_mutex_t& _mutex;//各个线程共同抢占同一把局部互斥锁
};

void BuyTicket(ThreadData* td)
{
    while(true)
    {
        LockGuard guard(&(td->_mutex));//guard为局部对象,当进入下一趟循环,guard会再次创建,构造时进行加锁
        //出该趟循环,guard对象自动销毁,析构时进行解锁      
        
        if(td->_tickets>0)//1.逻辑判断访问共享资源
        {
            usleep(1000);
            printf("%s get ticket number:%d\n",td->_threadname.c_str(),td->_tickets);//2. IO访问共享资源
            td->_tickets--;//3.算术运算访问共享资源
            td->_total++;
        }
        else
        {
            break;
        }
    }
}
int main()
{

    pthread_mutex_t mutex;//1. 创建局部互斥锁
    pthread_mutex_init(&mutex,nullptr);//2.初始化局部互斥锁


    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> tds;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        ThreadData* td=new ThreadData(g_tickets,threadname,mutex);
        threads.emplace_back(BuyTicket,td,threadname);
        tds.push_back(td);
    }

    for(auto& thread:threads)
    {
        thread.Start();
    }

     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
  
    for(auto td:tds)
    {
        cout<<td->_threadname<<" get total tickets:"<<td->_total<<endl;
        delete td;
    }

    pthread_mutex_destroy(&mutex);//5.销毁局部互斥锁
    return 0;
}
*/


/*
//解决方案二:采用局部互斥锁(使用封装的艺术)保护临界区实现对共享资源的保护
class ThreadData
{
public:
    ThreadData(int& g_tickets,const string& threadname,pthread_mutex_t& mutex)
    :_tickets(g_tickets)
    ,_threadname(threadname)
    ,_mutex(mutex)
    ,_total(0)
    {

    }
public:
    int& _tickets;//总票数,保证所有的线程都会引用同一个全局的g_tickets;
    string _threadname;//每个线程的线程名
    int _total;//每个线程获得的总票数
    pthread_mutex_t& _mutex;//各个线程共同抢占同一把局部互斥锁
};

void BuyTicket(ThreadData* td)
{
    while(true)
    {
        //3.加锁
        pthread_mutex_lock(&(td->_mutex));
        if(td->_tickets>0)//1.逻辑判断访问共享资源
        {
            usleep(1000);
            printf("%s get ticket number:%d\n",td->_threadname.c_str(),td->_tickets);//2. IO访问共享资源
            td->_tickets--;//3.算术运算访问共享资源
            //4.解锁
            pthread_mutex_unlock(&(td->_mutex));
            td->_total++;
        }
        else
        {
            pthread_mutex_unlock(&(td->_mutex));
            break;
        }
    }
}
int main()
{

    pthread_mutex_t mutex;//1. 创建局部互斥锁
    pthread_mutex_init(&mutex,nullptr);//2.初始化局部互斥锁


    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> tds;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        ThreadData* td=new ThreadData(g_tickets,threadname,mutex);
        threads.emplace_back(BuyTicket,td,threadname);
        tds.push_back(td);
    }

    for(auto& thread:threads)
    {
        thread.Start();
    }

     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
  
    for(auto td:tds)
    {
        cout<<td->_threadname<<" get total tickets:"<<td->_total<<endl;
        delete td;
    }

    pthread_mutex_destroy(&mutex);//5.销毁局部互斥锁
    return 0;
}
*/


/*
//解决方案一:采用全局互斥锁,保护临界区实现对共享资源的保护
class ThreadData
{
public:
    ThreadData(int& g_tickets,const string& threadname)
    :_tickets(g_tickets)
    ,_threadname(threadname)
    ,_total(0)
    {

    }
public:
    int& _tickets;//总票数,保证所有的线程都会引用同一个全局的g_tickets;
    string _threadname;//每个线程的线程名
    int _total;//每个线程获得的总票数
};

pthread_mutex_t g_mutex=PTHREAD_MUTEX_INITIALIZER;//全局互斥锁
void BuyTicket(ThreadData* td)
{
    //加锁的本质将多线程并行执行转换为串行执行,但是需要保证加锁的粒度越细越好！！！
    //线程访问临界区时,线程竞争锁是自由竞争的,若某一个线程竞争锁的能力太强,会导致其他线程抢不到锁从而导致线程饥饿问题！！！
    while(true)
    {
        pthread_mutex_lock(&g_mutex);//加锁
        if(td->_tickets>0)//1.逻辑判断访问共享资源
        {
            usleep(1000);
            printf("%s get ticket number:%d\n",td->_threadname.c_str(),td->_tickets);//2. IO访问共享资源
            td->_tickets--;//3.算术运算访问共享资源
            pthread_mutex_unlock(&g_mutex);//解锁
            td->_total++;
        }
        else
        {
            pthread_mutex_unlock(&g_mutex);//解锁
            break;
        }
    }
}
int main()
{
    //1.创建一批线程
    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> tds;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        ThreadData* td=new ThreadData(g_tickets,threadname);
        threads.emplace_back(BuyTicket,td,threadname);
        tds.push_back(td);
    }
    //2.启动一批线程
    for(auto& thread:threads)
    {
        thread.Start();
    }
    //3.等待一批线程
     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
    //4.输出各个线程的抢票总数
    for(auto td:tds)
    {
        cout<<td->_threadname<<" get total tickets:"<<td->_total<<endl;
        delete td;
    }

    return 0;
}
*/


//多个执行流并发访问共享资源,造成数据不一致;
/*
class ThreadData
{
public:
    ThreadData(int& g_tickets,const string& threadname)
    :_tickets(g_tickets)
    ,_threadname(threadname)
    ,_total(0)
    {

    }
public:
    int& _tickets;//总票数,保证所有的线程都会引用同一个全局的g_tickets;
    string _threadname;//每个线程的线程名
    int _total;//每个线程获得的总票数
};

void BuyTicket(ThreadData* td)
{
    //cout<<td->_threadname<<" &td->_tickets:"<<&td->_tickets<<endl;//验证各个线程使用同一个全局的g_tickets;
    while(true)
    {
        if(td->_tickets>0)
        {
            usleep(1000);
            printf("%s get ticket number:%d\n",td->_threadname.c_str(),td->_tickets);
            td->_tickets--;
            td->_total++;
        }
        else
        {
            break;
        }
    }
}
int main()
{
    //1.创建一批线程
    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> tds;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        ThreadData* td=new ThreadData(g_tickets,threadname);
        threads.emplace_back(BuyTicket,td,threadname);
        tds.push_back(td);
    }
    //2.启动一批线程
    for(auto& thread:threads)
    {
        thread.Start();
    }
    //3.等待一批线程
     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
    //4.输出各个线程的抢票总数
    for(auto td:tds)
    {
        cout<<td->_threadname<<" get total tickets:"<<td->_total<<endl;
        delete td;
    }

    return 0;
}
*/



/*
void BuyTicket(int& g_tickets)
{
    //cout<<"&g_tickets:"<<&g_tickets<<endl;
    while(true)
    {
        if(g_tickets>0)
        {
            usleep(1000);
            printf("get ticket number:%d\n",g_tickets);
            g_tickets--;
        }
        else
        {
            break;
        }
    }
}
int main()
{
    //cout<<"&g_tickets:"<<&g_tickets<<endl;
    //1.创建一批线程
    vector<Thread<int>> threads;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        threads.emplace_back(BuyTicket,g_tickets,threadname);
    }
    //2.启动一批线程
    for(auto& thread:threads)
    {
        thread.Start();
    }
    //3.等待一批线程
     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
}
*/


/*
void threadrun(int num)
{
    while(num)
    {
        cout<<"new thread is running"<<endl;
        sleep(1);
        num--;
    }

}
int main()
{
    //1.创建一批线程
    vector<Thread<int>> threads;
    for(int i=0;i<threadnum;i++)
    {
        string threadname="thread -"+to_string(i+1);
        threads.emplace_back(threadrun,10,threadname);
    }
    //2.启动一批线程
    for(auto& thread:threads)
    {
        thread.Start();
    }
    //3.等待一批线程
     for(auto& thread:threads)
    {
        thread.Join();
        cout<<"wait sucess,threadname:"<<thread.threadname()<<endl;
    }
}

*/


/*
void threadrun(int num)
{
    while(num)
    {
        cout<<"thread is running"<<endl;
        sleep(1);
        num--;
    }
}

int main()
{
    Thread<int> t1(threadrun,10);
    t1.Start();

    t1.Join();
    return 0;
}
*/